﻿#include "stdafx.h"

#include "utils.h"

#include <QtConcurrent/QtConcurrentRun>
#include "gsl/gsl"

namespace
{
	template <typename T>
	using PaintFun = std::function<void(QPainter&, T)>;
	template<typename T, typename R>
	void _ParallelPaint(const gsl::span<T>& shapes, QPixmap& target, const PainterInitFun& painterInit, const PaintFun<R>& paint)
	{
		auto paintToPixmap = [&](size_t idx, size_t end)
		{
			QPixmap pixmap(target.width(), target.height()); // 第一张图片其实可以复用 target
			pixmap.fill(Qt::transparent);
			QPainter painter(&pixmap);
			painterInit(painter);

			for (; idx < end; ++idx)
				paint(painter, shapes[idx]);

			return pixmap;
		};

		size_t threadNum = 1;
		if (shapes.size() < std::thread::hardware_concurrency() * 100)
			threadNum = shapes.size() / 100 + 1;
		else
			threadNum = std::thread::hardware_concurrency();

		const size_t oneThreadNum = shapes.size() / threadNum + 1;

		size_t start = 0;
		std::vector<QFuture<QPixmap>> results;
		for (int i = 0; i < threadNum; ++i)
		{
			size_t end = start + oneThreadNum;
			if (end > shapes.size())
				end = shapes.size();
			results.push_back(QtConcurrent::run(paintToPixmap, start, end));

			start = end;
		}
		ASSERT(start == shapes.size());

		QPainter painter(&target);
		for (const auto& future : results)
			painter.drawPixmap(0, 0, future.result());
	}
}

QRectF EnlargeRect(QRectF rect, double size)
{
	rect.translate(-size, -size);
	rect.setWidth((rect.width()) + size * 2);
	rect.setHeight((rect.height()) + size * 2);
	return rect;
}

QRectF CreateRectF(const QPointF& p0, const QPointF& p1)
{
	const QPointF leftTop = { std::min(p0.x(), p1.x()),
		std::min(p0.y(), p1.y()) };
	const QPointF rightBottom = { std::max(p0.x(), p1.x()),
		std::max(p0.y(), p1.y()) };
	return { leftTop, rightBottom };
}

void ParallelPaint(const gsl::span<SvgShape*> shapes, QPixmap& target, const PainterInitFun& painterInit)
{
	const PaintFun<SvgShape*> paint = [](QPainter& painter, const SvgShape* shape) { shape->Paint(painter); };
	_ParallelPaint(shapes, target, painterInit, paint);
}

void ParallelPaint(const gsl::span<unique_ptr<SvgShape>> shapes, QPixmap& target, const PainterInitFun& painterInit)
{
	const PaintFun<const unique_ptr<SvgShape>&> paint = [](QPainter& painter, const unique_ptr<SvgShape>& shape) { shape->Paint(painter); };
	_ParallelPaint(shapes, target, painterInit, paint);
}

void ParallelPaint(const gsl::span<QRectF> shapes, QPixmap& target, const PainterInitFun& painterInit)
{
	const PaintFun<const QRectF&> paint = [](QPainter& painter, const QRectF& rect) { painter.drawRect(rect); };
	_ParallelPaint(shapes, target, painterInit, paint);
}
